# -*- coding: binary -*-
module Msf
module Ui
module Console
module CommandDispatcher

###
#
# Exploit module command dispatcher.
#
###
class Exploit

  include Msf::Ui::Console::ModuleCommandDispatcher

  @@exploit_opts = Rex::Parser::Arguments.new(
    "-e" => [ true,  "The payload encoder to use.  If none is specified, ENCODER is used." ],
    "-f" => [ false, "Force the exploit to run regardless of the value of MinimumRank."    ],
    "-h" => [ false, "Help banner."                                                        ],
    "-j" => [ false, "Run in the context of a job."                                        ],
    "-n" => [ true,  "The NOP generator to use.  If none is specified, NOP is used."       ],
    "-o" => [ true,  "A comma separated list of options in VAR=VAL format."                ],
    "-p" => [ true,  "The payload to use.  If none is specified, PAYLOAD is used."         ],
    "-t" => [ true,  "The target index to use.  If none is specified, TARGET is used."     ],
    "-z" => [ false, "Do not interact with the session after successful exploitation."     ])

  #
  # Returns the hash of exploit module specific commands.
  #
  def commands
    super.update({
      "exploit"  => "Launch an exploit attempt",
      "rcheck"   => "Reloads the module and checks if the target is vulnerable",
      "rexploit" => "Reloads the module and launches an exploit attempt",
      "run"      => "Alias for exploit",
      "recheck"  => "Alias for rcheck",
      "rerun"    => "Alias for rexploit",
      "reload"   => "Just reloads the module"
    })
  end

  #
  # Returns the name of the command dispatcher.
  #
  def name
    "Exploit"
  end

  #
  # Launches an exploitation attempt.
  #
  def cmd_exploit(*args)
    opt_str = nil
    payload = mod.datastore['PAYLOAD']
    encoder = mod.datastore['ENCODER']
    target  = mod.datastore['TARGET']
    nop     = mod.datastore['NOP']
    bg      = false
    jobify  = false
    force   = false

    # Always run passive exploits in the background
    if (mod.passive?)
      jobify = true
    end

    @@exploit_opts.parse(args) { |opt, idx, val|
      case opt
        when '-e'
          encoder = val
        when '-f'
          force = true
        when '-j'
          jobify = true
        when '-n'
          nop = val
        when '-o'
          opt_str = val
        when '-p'
          payload = val
        when '-t'
          target = val.to_i
        when '-z'
          bg = true
        when '-h'
          cmd_exploit_help
          return false
      end
    }

    minrank = RankingName.invert[framework.datastore['MinimumRank']] || 0
    if minrank > mod.rank
      if force
        print_status("Forcing #{mod.refname} to run despite MinimumRank '#{framework.datastore['MinimumRank']}'")
        ilog("Forcing #{mod.refname} to run despite MinimumRank '#{framework.datastore['MinimumRank']}'", 'core')
      else
        print_error("This exploit is below the minimum rank, '#{framework.datastore['MinimumRank']}'.")
        print_error("If you really want to run it, do 'exploit -f' or")
        print_error("setg MinimumRank to something lower ('manual' is")
        print_error("the lowest and would allow running all exploits).")
        return
      end
    end

    if not payload
      payload = Exploit.choose_payload(mod, target)
    end

    begin
      session = mod.exploit_simple(
        'Encoder'        => encoder,
        'Payload'        => payload,
        'Target'         => target,
        'Nop'            => nop,
        'OptionStr'      => opt_str,
        'LocalInput'     => driver.input,
        'LocalOutput'    => driver.output,
        'RunAsJob'       => jobify)
    rescue ::Interrupt
      raise $!
    rescue ::Exception => e
      print_error("Exploit exception (#{mod.refname}): #{e.class} #{e}")
      if(e.class.to_s != 'Msf::OptionValidateError')
        print_error("Call stack:")
        e.backtrace.each do |line|
          break if line =~ /lib.msf.base.simple/
          print_error("  #{line}")
        end
      end
    end

    # If we were given a session, let's see what we can do with it
    if (session)

      # If we aren't told to run in the background and the session can be
      # interacted with, start interacting with it by issuing the session
      # interaction command.
      if (bg == false and session.interactive?)
        print_line

        driver.run_single("sessions -q -i #{session.sid}")
      # Otherwise, log that we created a session
      else
        print_status("Session #{session.sid} created in the background.")
      end
    # If we ran the exploit as a job, indicate such so the user doesn't
    # wonder what's up.
    elsif (jobify && mod.job_id)
      print_status("Exploit running as background job #{mod.job_id}.")
    # Worst case, the exploit ran but we got no session, bummer.
    else
      # If we didn't run a payload handler for this exploit it doesn't
      # make sense to complain to the user that we didn't get a session
      unless mod.datastore["DisablePayloadHandler"]
        fail_msg = 'Exploit completed, but no session was created.'
        print_status(fail_msg)
        begin
          framework.events.on_session_fail(fail_msg)
        rescue ::Exception => e
          wlog("Exception in on_session_open event handler: #{e.class}: #{e}")
          wlog("Call Stack\n#{e.backtrace.join("\n")}")
        end
      end
    end
  end

  def cmd_exploit_tabs(str, words)
    fmt = {
      '-e' => [ framework.encoders.map { |refname, mod| refname } ],
      '-f' => [ nil                                               ],
      '-h' => [ nil                                               ],
      '-j' => [ nil                                               ],
      '-n' => [ framework.nops.map { |refname, mod| refname }     ],
      '-o' => [ true                                              ],
      '-p' => [ framework.payloads.map { |refname, mod| refname } ],
      '-t' => [ true                                              ],
      '-z' => [ nil                                               ]
    }
    tab_complete_generic(fmt, str, words)
  end

  alias cmd_run cmd_exploit

  def cmd_exploit_help
    print_line "Usage: exploit [options]"
    print_line
    print_line "Launches an exploitation attempt."
    print @@exploit_opts.usage
  end

  alias cmd_run_help cmd_exploit_help

  #
  # Reloads an exploit module and checks the target to see if it's
  # vulnerable.
  #
  def cmd_rcheck(*args)
    reload()

    cmd_check(*args)
  end

  alias cmd_recheck cmd_rcheck

  #
  # Reloads an exploit module and launches an exploit.
  #
  def cmd_rexploit(*args)
    return cmd_rexploit_help if args.include? "-h"

    # Stop existing job and reload the module
    if reload(true)
      # Delegate to the exploit command unless the reload failed
      cmd_exploit(*args)
    end
  end

  alias cmd_rerun cmd_rexploit

  def cmd_rexploit_help
    print_line "Usage: rexploit [options]"
    print_line
    print_line "Reloads a module, stopping any associated job, and launches an exploitation attempt."
    print @@exploit_opts.usage
  end

  alias cmd_rerun_help cmd_rexploit_help

  #
  # Picks a reasonable payload and minimally configures it
  #
  def self.choose_payload(mod, target)

    # Choose either the real target or an invalid address
    # This is used to determine the LHOST value
    rhost = mod.datastore['RHOST'] || '50.50.50.50'

    # A list of preferred payloads in the best-first order
    pref = [
      'windows/meterpreter/reverse_tcp',
      'linux/x86/meterpreter/reverse_tcp',
      'java/meterpreter/reverse_tcp',
      'php/meterpreter/reverse_tcp',
      'php/meterpreter_reverse_tcp',
      'ruby/shell_reverse_tcp',
      'nodejs/shell_reverse_tcp',
      'cmd/unix/interact',
      'cmd/unix/reverse',
      'cmd/unix/reverse_perl',
      'cmd/unix/reverse_netcat_gaping',
      'windows/meterpreter/reverse_nonx_tcp',
      'windows/meterpreter/reverse_ord_tcp',
      'windows/shell/reverse_tcp',
      'generic/shell_reverse_tcp'
    ]
    pset = mod.compatible_payloads.map{|x| x[0] }
    pref.each do |n|
      if(pset.include?(n))
        mod.datastore['PAYLOAD'] = n
        mod.datastore['LHOST']   = Rex::Socket.source_address(rhost)
        return n
      end
    end
    return
  end

end

end end end end
